package com.pig4cloud.pigx.contract.websocket.subscribe;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.pig4cloud.pigx.common.websocket.holder.SessionKeyGenerator;
import com.pig4cloud.pigx.contract.dto.AppContractOptionalSymbolListDTO;
import com.pig4cloud.pigx.contract.entity.ContractOptionalSymbol;
import com.pig4cloud.pigx.contract.mapper.ContractOptionalSymbolMapper;
import com.pig4cloud.pigx.contract.utils.ContractUtils;
import com.pig4cloud.pigx.contract.vo.AbstractTopicVO;
import com.pig4cloud.pigx.contract.vo.AppContractSymbolIncreaseRankingVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.socket.WebSocketSession;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 */
@Slf4j
@Component
public class OptionalSymbol extends DataListAbstractTopic {
	private final MongoTemplate mongo;
	private final SessionKeyGenerator sessionKeyGenerator;
	private final ContractOptionalSymbolMapper symbolMapper;
	private final ContractUtils utils;

	protected String TOPICNAME = "optionalSymbol";

	public OptionalSymbol(MongoTemplate mongo, SessionKeyGenerator sessionKeyGenerator, ContractOptionalSymbolMapper symbolMapper, ContractUtils utils) {
		this.mongo = mongo;
		this.sessionKeyGenerator = sessionKeyGenerator;
		this.symbolMapper = symbolMapper;
		this.utils = utils;
		init();
	}

	private void init() {
		wsPush();
	}

	private void wsPush() {
		ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
		service.scheduleAtFixedRate(() -> {
			try {
				for (Map.Entry<WebSocketSession, AbstractTopicVO> entry : subList.entrySet()) {
					sendMessage(entry);
				}
			} catch (Throwable e) {
				e.printStackTrace();
				log.info("push optional symbol err:" + e.getMessage());
			}

		}, 0, 2000, TimeUnit.MILLISECONDS);
	}


	public void sendMessage(Map.Entry<WebSocketSession, AbstractTopicVO> entry) {
		try {
			if (!entry.getKey().isOpen()) {
				subList.remove(entry.getKey());
				log.info("ws 移除已关闭的session");
				return;
			}

			AbstractTopicVO param = entry.getValue();
			List<String> dataList = param.getDataList();

			Object userId = sessionKeyGenerator.sessionKey(entry.getKey());

			AppContractOptionalSymbolListDTO dto = new AppContractOptionalSymbolListDTO();
			dto.setType(String.valueOf(dataList.get(1)));

			// 设置排序方式
			int orderField = Integer.parseInt(dataList.get(2));
			Integer orderType = Integer.valueOf(Strings.isNotEmpty(dataList.get(3)) ? dataList.get(3) : "0");
			switch (orderField) {
				case 0:
					dto.setOrderSumPrice(orderType);
					break;
				case 1:
					dto.setOrderLastPrice(orderType);
					break;
				case 2:
					dto.setOrderRiseAndFallRange(orderType);
					break;
				default:
					break;
			}

			Query query = new Query();
			query.addCriteria(Criteria.where("status").is("1"));

			// 查询自选币对的idList
			QueryWrapper<ContractOptionalSymbol> queryWrapper = new QueryWrapper<>();
			queryWrapper.lambda()
					.select(ContractOptionalSymbol::getSymbolId)
					.eq(ContractOptionalSymbol::getUserId, userId);
			List<ContractOptionalSymbol> optionalList = symbolMapper.selectList(queryWrapper);

			// 将自选币对idList作为查询条件
			List<Long> symbolIdList = optionalList.stream()
					.map(ContractOptionalSymbol::getSymbolId)
					.collect(Collectors.toList());

			// 查询类型 = 自选币对
			if (Objects.equals(AppContractOptionalSymbolListDTO.TYPE_OPTIONAL, dto.getType())) {
				if (CollectionUtils.isEmpty(optionalList)) {
					// 直接返回空列表
					JSONArray array = JSONArray.parseArray(JSON.toJSONString(Collections.EMPTY_LIST));
					entry.getKey().sendMessage(getTextMessage(dataList, array));
					return;

				} else {
					query.addCriteria(Criteria.where("_id").in(symbolIdList));
				}
			}

			// 处理排序规则
			List<Sort.Order> orderList = this.getOrders(dto);
			// 如果指定了排序
			if (!CollectionUtils.isEmpty(orderList)) {
				query.with(Sort.by(orderList));
			}

			// 分页
			List<AppContractSymbolIncreaseRankingVO> voList = mongo.find(query, AppContractSymbolIncreaseRankingVO.class);
			// 遍历
			voList.forEach(vo -> {
				// 设置精度
				utils.setScaleBySymbol(vo, vo.getSymbolName());

				// 设置是否自选
				vo.setIsOptional(symbolIdList.contains(Long.valueOf(vo.getSymbolId())));
			});

			JSONArray array = JSONArray.parseArray(JSON.toJSONString(voList));
			entry.getKey().sendMessage(getTextMessage(dataList, array));
		} catch (Throwable e) {
			e.printStackTrace();
			log.info("push optional symbol err:" + e.getMessage());
		} finally {
			// 清除symbol缓存
			ContractUtils.removeSymbolThreadCache();
		}
	}

	/**
	 * 拼接排序规则
	 *
	 * @param data 参数
	 * @return
	 */
	private List<Sort.Order> getOrders(AppContractOptionalSymbolListDTO data) {
		List<Sort.Order> orderList = new ArrayList<>();
		// 按成交量排序
		if (data.getOrderSumPrice() != null) {
			if (data.getOrderSumPrice() == 0) {
				orderList.add(Sort.Order.asc("sumPrice"));
			} else {
				orderList.add(Sort.Order.desc("sumPrice"));
			}
		}
		// 按最新价排序
		else if (data.getOrderLastPrice() != null) {
			if (data.getOrderLastPrice() == 0) {
				orderList.add(Sort.Order.asc("lastPrice"));
			} else {
				orderList.add(Sort.Order.desc("lastPrice"));
			}
		}
		// 按涨跌幅排序
		else if (data.getOrderRiseAndFallRange() != null) {
			if (data.getOrderRiseAndFallRange() == 0) {
				orderList.add(Sort.Order.asc("riseAndFallRange"));
			} else {
				orderList.add(Sort.Order.desc("riseAndFallRange"));
			}
		} else {
			orderList.add(Sort.Order.asc("orderNo"));
		}
		return orderList;
	}
}
